#undef BLENDSTRETCH
#undef AREAAVERAGING32
#undef AREAAVERAGING64

#define BLENDSTRETCH MAKE_BLEND(BLEND_MODE, PIXEL_BITS, StretchAreaAveraging, )
#define AREAAVERAGING32 MAKE_BLENDAREAAVERAGING32(BLEND_MODE, PIXEL_BITS, )
#define AREAAVERAGING64 MAKE_BLENDAREAAVERAGING64(BLEND_MODE, PIXEL_BITS, )

void AREAAVERAGING64(const int left, const int right, const int top, const int bottom, const DWORD xrIntFloat16, const DWORD yrIntFloat16, PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
    PBYTE pBitsSrc = (BYTE*)lpbSrc->bmBits;
    DWORD nWidthBytesSrc = lpbSrc->bmWidthBytes;

    for (int y = top; y < bottom; y++)
    {
        LPXARGB pBitsDst = (LPXARGB)((LPBYTE)lpbDst->bmBits + (y - top) * lpbDst->bmWidthBytes);
        int dY = y * yrIntFloat16 + (yrIntFloat16 >> 1) - 0x8000;
        int t = dY - (yrIntFloat16 >> 1);
        int b = dY + (yrIntFloat16 >> 1);
        int yi1 = (t + 0x8000) >> 16;
        int yi2 = (b + 0x8000) >> 16;
        DWORD h1 = min((yi1 << 16) + 0x8000, b) - max((yi1 << 16) - 0x8000, t);
        DWORD h2 = min((yi2 << 16) + 0x8000, b) - max((yi2 << 16) - 0x8000, t);
        yi1 = max(yi1, 0);
        yi2 = min(yi2, lpbSrc->bmHeight - 1);

        for (int x = left; x < right; x++)
        {
            int dX = x * xrIntFloat16 + (xrIntFloat16 >> 1) - 0x8000;
            int l = dX - (xrIntFloat16 >> 1);
            int r = dX + (xrIntFloat16 >> 1);
            int xi1 = (l + 0x8000) >> 16;
            int xi2 = (r + 0x8000) >> 16;

            DWORD w1 = min((xi1 << 16) + 0x8000, r) - max((xi1 << 16) - 0x8000, l);
            DWORD w2 = min((xi2 << 16) + 0x8000, r) - max((xi2 << 16) - 0x8000, l);
            xi1 = max(xi1, 0);
            xi2 = min(xi2, lpbSrc->bmWidth - 1);

            UINT64 rr, gg, bb, aa;
            rr = gg = bb = aa = 0;
            UINT64 s = 0;

            PIXEL* pBits;
            pBits = (PIXEL*)(pBitsSrc + yi1 * nWidthBytesSrc);
            AddColorValue64(GETPIXEL(lpbSrc, PBYTE(pBits + xi1)), (UINT64)w1 * (UINT64)h1 >> 16, bb, gg, rr, aa, s);
            AddColorValue64(GETPIXEL(lpbSrc, PBYTE(pBits + xi2)), (UINT64)w2 * (UINT64)h1 >> 16, bb, gg, rr, aa, s);
            pBits = (PIXEL*)(pBitsSrc + yi2 * nWidthBytesSrc);
            AddColorValue64(GETPIXEL(lpbSrc, PBYTE(pBits + xi1)), (UINT64)w1 * (UINT64)h2 >> 16, bb, gg, rr, aa, s);
            AddColorValue64(GETPIXEL(lpbSrc, PBYTE(pBits + xi2)), (UINT64)w2 * (UINT64)h2 >> 16, bb, gg, rr, aa, s);

            for (int j = xi1 + 1; j < xi2; j++)
            {
                pBits = (PIXEL*)(pBitsSrc + yi1 * nWidthBytesSrc);
                AddColorValue64(GETPIXEL(lpbSrc, PBYTE(pBits + j)), h1, bb, gg, rr, aa, s);
                pBits = (PIXEL*)(pBitsSrc + yi2 * nWidthBytesSrc);
                AddColorValue64(GETPIXEL(lpbSrc, PBYTE(pBits + j)), h2, bb, gg, rr, aa, s);
            }

            for (int i = yi1 + 1; i < yi2; i++)
            {
                pBits = (PIXEL*)(pBitsSrc + i * nWidthBytesSrc);
                AddColorValue64(GETPIXEL(lpbSrc, PBYTE(pBits + xi1)), w1, bb, gg, rr, aa, s);
                AddColorValue64(GETPIXEL(lpbSrc, PBYTE(pBits + xi2)), w2, bb, gg, rr, aa, s);
                for (int j = xi1 + 1; j < xi2; j++)
                {
                    xARGB clr = GETPIXEL(lpbSrc, PBYTE(pBits + j));
                    bb += (clr.blue << 16);
                    gg += (clr.green << 16);
                    rr += (clr.red << 16);
                    aa += (clr.alpha << 16);
                    s += (1 << 16);
                }
            }
            xARGB clr;
            clr.blue = (BYTE)(bb / s);
            clr.green = (BYTE)(gg / s);
            clr.red = (BYTE)(rr / s);
            clr.alpha = (BYTE)(aa / s);
            BLENDPIXEL(&pBitsDst[x - left], clr);
        }
    }
}

void AREAAVERAGING32(const int left, const int right, const int top, const int bottom, const DWORD xrIntFloat16, const DWORD yrIntFloat16, PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
    BYTE* pBitsSrc = (BYTE*)lpbSrc->bmBits;
    DWORD nWidthBytesSrc = lpbSrc->bmWidthBytes;

    for (int y = top; y < bottom; y++)
    {
        LPXARGB pBitsDst = (LPXARGB)((LPBYTE)lpbDst->bmBits + (y - top) * lpbDst->bmWidthBytes);
        int dY = y * yrIntFloat16 + (yrIntFloat16 >> 1) - 0x8000;
        int t = dY - (yrIntFloat16 >> 1);
        int b = dY + (yrIntFloat16 >> 1);
        int yi1 = (t + 0x8000) >> 16;
        int yi2 = (b + 0x8000) >> 16;
        DWORD h1 = min((yi1 << 16) + 0x8000, b) - max((yi1 << 16) - 0x8000, t);
        DWORD h2 = min((yi2 << 16) + 0x8000, b) - max((yi2 << 16) - 0x8000, t);
        yi1 = max(yi1, 0);
        yi2 = min(yi2, lpbSrc->bmHeight - 1);

        for (int x = left; x < right; x++)
        {
            int dX = x * xrIntFloat16 + (xrIntFloat16 >> 1) - 0x8000;
            int l = dX - (xrIntFloat16 >> 1);
            int r = dX + (xrIntFloat16 >> 1);
            int xi1 = (l + 0x8000) >> 16;
            int xi2 = (r + 0x8000) >> 16;

            DWORD w1 = min((xi1 << 16) + 0x8000, r) - max((xi1 << 16) - 0x8000, l);
            DWORD w2 = min((xi2 << 16) + 0x8000, r) - max((xi2 << 16) - 0x8000, l);
            xi1 = max(xi1, 0);
            xi2 = min(xi2, lpbSrc->bmWidth - 1);

            DWORD rr, gg, bb, aa;
            rr = gg = bb = aa = 0;
            DWORD s = 0;

            PIXEL* pBits;
            pBits = (PIXEL*)(pBitsSrc + yi1 * nWidthBytesSrc);
            AddColorValue32(GETPIXEL(lpbSrc, PBYTE(pBits + xi1)), (UINT64)w1 * (UINT64)h1 >> 16, bb, gg, rr, aa, s);
            AddColorValue32(GETPIXEL(lpbSrc, PBYTE(pBits + xi2)), (UINT64)w2 * (UINT64)h1 >> 16, bb, gg, rr, aa, s);
            pBits = (PIXEL*)(pBitsSrc + yi2 * nWidthBytesSrc);
            AddColorValue32(GETPIXEL(lpbSrc, PBYTE(pBits + xi1)), (UINT64)w1 * (UINT64)h2 >> 16, bb, gg, rr, aa, s);
            AddColorValue32(GETPIXEL(lpbSrc, PBYTE(pBits + xi2)), (UINT64)w2 * (UINT64)h2 >> 16, bb, gg, rr, aa, s);

            for (int j = xi1 + 1; j < xi2; j++)
            {
                pBits = (PIXEL*)(pBitsSrc + yi1 * nWidthBytesSrc);
                AddColorValue32(GETPIXEL(lpbSrc, PBYTE(pBits + j)), h1, bb, gg, rr, aa, s);
                pBits = (PIXEL*)(pBitsSrc + yi2 * nWidthBytesSrc);
                AddColorValue32(GETPIXEL(lpbSrc, PBYTE(pBits + j)), h2, bb, gg, rr, aa, s);
            }

            for (int i = yi1 + 1; i < yi2; i++)
            {
                pBits = (PIXEL*)(pBitsSrc + i * nWidthBytesSrc);
                AddColorValue32(GETPIXEL(lpbSrc, PBYTE(pBits + xi1)), w1, bb, gg, rr, aa, s);
                AddColorValue32(GETPIXEL(lpbSrc, PBYTE(pBits + xi2)), w2, bb, gg, rr, aa, s);
                for (int j = xi1 + 1; j < xi2; j++)
                {
                    xARGB clr = GETPIXEL(lpbSrc, PBYTE(pBits + j));
                    bb += (clr.blue << 16);
                    gg += (clr.green << 16);
                    rr += (clr.red << 16);
                    aa += (clr.alpha << 16);
                    s += (1 << 16);
                }
            }
            xARGB clr;
            clr.blue = (BYTE)(bb / s);
            clr.green = (BYTE)(gg / s);
            clr.red = (BYTE)(rr / s);
            clr.alpha = (BYTE)(aa / s);
            BLENDPIXEL(&pBitsDst[x - left], clr);
        }
    }
}

#define BLENDSTRETCHBICUBIC MAKE_BLEND(BLEND_MODE, PIXEL_BITS, StretchBicubic, )
#define BLENDSTRETCHBICUBICMMX MAKE_BLEND(BLEND_MODE, PIXEL_BITS, StretchBicubic, MMX)

BOOL BLENDSTRETCH(PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
    if (lpbSrc->bmWidth < lpbDst->bmWidth || lpbSrc->bmHeight < lpbDst->bmHeight)
    {
        if (xCPUInfo::IsSupportMMX())
            return BLENDSTRETCHBICUBICMMX(lpbDst, lpbSrc);
        else
            return BLENDSTRETCHBICUBIC(lpbDst, lpbSrc);
    }
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpbDst->bmWidth;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpbDst->bmHeight;

    if (xrIntFloat16 > 0x100000 || yrIntFloat16 > 0x100000)
    {
        AREAAVERAGING64(0, lpbDst->bmWidth, 0, lpbDst->bmHeight, xrIntFloat16, yrIntFloat16, lpbDst, lpbSrc);
    }
    else
    {
        AREAAVERAGING32(0, lpbDst->bmWidth, 0, lpbDst->bmHeight, xrIntFloat16, yrIntFloat16, lpbDst, lpbSrc);
    }
    return TRUE;
}

BOOL BLENDSTRETCH(PBITMAP lpbDst, PBITMAPEX lpbSrc, LPCSIZE lpszDst, LPCRECT lprcClipped)
{
    if (lpbSrc->bmWidth < lpszDst->cx || lpbSrc->bmHeight < lpszDst->cy)
    {
        if (xCPUInfo::IsSupportMMX())
            return BLENDSTRETCHBICUBICMMX(lpbDst, lpbSrc, lpszDst, lprcClipped);
        else
            return BLENDSTRETCHBICUBIC(lpbDst, lpbSrc, lpszDst, lprcClipped);
    }
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpszDst->cx;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpszDst->cy;

    if (xrIntFloat16 > 0x100000 || yrIntFloat16 > 0x100000)
    {
        AREAAVERAGING64(lprcClipped->left, lprcClipped->right, lprcClipped->top, lprcClipped->bottom, xrIntFloat16, yrIntFloat16, lpbDst, lpbSrc);
    }
    else
    {
        AREAAVERAGING32(lprcClipped->left, lprcClipped->right, lprcClipped->top, lprcClipped->bottom, xrIntFloat16, yrIntFloat16, lpbDst, lpbSrc);
    }
    return TRUE;
}

#undef BLENDSTRETCHBICUBIC